package com.vision.sales.deliveryreceipt.cl.ui;

import static com.vision.sales.cl.SalesServices.customerSvc;
import static com.vision.sales.cl.SalesServices.deliveryReceiptSvc;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.SimpleBeanEditorDriver;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.data.client.loader.RpcProxy;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.SortDir;
import com.sencha.gxt.data.shared.Store;
import com.sencha.gxt.data.shared.Store.StoreSortInfo;
import com.sencha.gxt.data.shared.loader.LoadEvent;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.widget.core.client.TabItemConfig;
import com.sencha.gxt.widget.core.client.Window;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.ParseErrorEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.form.ComboBox;
import com.sencha.gxt.widget.core.client.form.DateField;
import com.sencha.gxt.widget.core.client.form.StoreFilterField;
import com.sencha.gxt.widget.core.client.form.TextArea;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.form.validator.MaxLengthValidator;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.info.Info;
import com.sencha.gxt.widget.core.client.menu.CheckMenuItem;
import com.sencha.gxt.widget.core.client.menu.Item;
import com.sencha.gxt.widget.core.client.menu.MenuItem;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar;
import com.vision.core.cl.BaseModule;
import com.vision.core.cl.Callback;
import com.vision.core.cl.event.CreatedEvent;
import com.vision.core.cl.event.CreatedEvent.CreationHandler;
import com.vision.core.cl.event.DeletedEvent;
import com.vision.core.cl.event.DeletedEvent.DeletionHandler;
import com.vision.core.cl.ui.ConfirmDialogHandler;
import com.vision.core.cl.ui.Notifications;
import com.vision.core.cl.ui.UI;
import com.vision.core.cl.ui.component.BasicGridView;
import com.vision.core.cl.ui.component.BasicPagingLoader;
import com.vision.core.cl.ui.component.MultiSelectListAndDetailPanel;
import com.vision.sales.cl.SalesServices;
import com.vision.sales.cl.SalesTasks;
import com.vision.sales.cl.data.DeliveryReceiptProperties;
import com.vision.sales.cl.event.DeliveryReceiptCreatedEvent;
import com.vision.sales.cl.event.SalesInvoiceCreatedEvent;
import com.vision.sales.cl.event.SalesInvoiceDeletedEvent;
import com.vision.sales.cl.resource.SalesResources;
import com.vision.sales.cl.ui.component.TransactionLoadConfig;
import com.vision.sales.cm.db.data.Customer;
import com.vision.sales.cm.db.data.DeliveryReceipt;
import com.vision.sales.cm.db.data.DeliveryReceipt.FieldSpecs;
import com.vision.sales.cm.db.data.SalesInvoice;

/**
 * 
 * @author Mark
 *
 */
public class DeliveryReceiptsPanel extends MultiSelectListAndDetailPanel<DeliveryReceipt> implements Editor<DeliveryReceipt> {

	interface Binder extends UiBinder<Widget, DeliveryReceiptsPanel> { }

	private static final Binder binder = GWT.create(Binder.class);
	
	@UiField VerticalLayoutContainer mainPanel;
	
	// Ignored by the form driver since these are not fields in the DeliveryReceipt
	@Ignore @UiField StoreFilterField<DeliveryReceipt> gridFilter;
	@Ignore @UiField TextButton filterBtn;
		@Ignore @UiField MenuItem statusMenuItem;
			@Ignore @UiField CheckMenuItem uninvoicedOnlyMenuItem;
			@Ignore @UiField CheckMenuItem invoicedOnlyMenuItem;
			@Ignore @UiField CheckMenuItem allStatusMenuItem;
	
	@Ignore @UiField TextButton addBtn;
	@Ignore @UiField TextButton createInvoiceBtn;
	@Ignore @UiField TextButton moreBtn;
	@Ignore @UiField MenuItem refreshMenuItem;
	@Ignore @UiField MenuItem voidMenuItem;
	@Ignore @UiField MenuItem deleteMenuItem;
	
	@UiField Grid<DeliveryReceipt> grid;
	@UiField PagingToolBar pagingToolbar;
	PagingLoader<PagingLoadConfig, PagingLoadResult<DeliveryReceipt>> loader;
	
	// Form fields that are bound by the form driver to the fields in the DeliveryReceipt class.
	// The name of these form fields must match the name of the field in the DeliveryReceipt class.
	@UiField TextField drNo;
	@UiField DateField date;
	@UiField ComboBox<Customer> customer;
	@UiField ComboBox<SalesInvoice> salesInvoice;
	@UiField TextArea remarks;
	
	// Ignored by the form driver since these are not fields in the DeliveryReceipt
	@Ignore @UiField TextButton saveBtn;
	@Ignore @UiField TextButton cancelBtn;
	
	// Interface used to bind the form fields to the DeliveryReceipt fields
	interface DeliveryReceiptDriver extends SimpleBeanEditorDriver<DeliveryReceipt, DeliveryReceiptsPanel> { }
	
	// Instance of the DeliveryReceiptDriver interface
	private DeliveryReceiptDriver driver = GWT.create(DeliveryReceiptDriver.class);
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public DeliveryReceiptsPanel() {
		// Global Handlers -- should be registered only once
		BaseModule.addHandler(DeliveryReceiptCreatedEvent.TYPE, new CreationHandler<DeliveryReceipt>() {
			@Override
			public void onCreated(CreatedEvent<DeliveryReceipt> e) {
				grid.getStore().add(e.getObject());
			}
		});
		
		BaseModule.addHandler(SalesInvoiceDeletedEvent.TYPE, new DeletionHandler<SalesInvoice>() {
			@Override
			public void onDeleted(DeletedEvent<SalesInvoice> e) {
				// Find all Delivery Receipts with the deleted invoice
				List<DeliveryReceipt> drs = grid.getStore().getAll();
				for (DeliveryReceipt dr : drs) {
					if (dr.getSalesInvoice() != null && dr.getSalesInvoice().equals(e.getObject())) {
						dr.setSalesInvoice(null); // Set to null
						grid.getStore().update(dr);
					}
				}
			}
		});
		
		BaseModule.addHandler(SalesInvoiceCreatedEvent.TYPE, new CreationHandler<SalesInvoice>() {
			@Override
			public void onCreated(CreatedEvent<SalesInvoice> e) {
				SalesInvoice invoice = e.getObject();
				Set<DeliveryReceipt> receipts = invoice.getDeliveryReceipts();
				for (DeliveryReceipt receipt : receipts) {
					grid.getStore().update(receipt);
				}
			}
		});
		
		setWidget(binder.createAndBindUi(this));
		
		// Bind the filter into the grid's store
		gridFilter.bind(grid.getStore());
		
		date.setPropertyEditor(UI.datePropertyEditor());
		
		// Initialize the driver, used to run the form editor
		driver.initialize(this);
		
		drNo.addValidator(new MaxLengthValidator(FieldSpecs.DR_NO_MAX));
		remarks.addValidator(new MaxLengthValidator(FieldSpecs.REMARKS_MAX));
		
		// Initialize loaders
		RpcProxy<PagingLoadConfig, PagingLoadResult<DeliveryReceipt>> proxy = new RpcProxy<PagingLoadConfig, PagingLoadResult<DeliveryReceipt>>() {
		    @Override
		    public void load(PagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<DeliveryReceipt>> callback) {
		    	TransactionLoadConfig tConfig = new TransactionLoadConfig(loadConfig);
		    	setFilterInfo(tConfig);
		    	SalesServices.deliveryReceiptSvc().getList(tConfig, callback);
		    }
	    };
	    
	    loader = new BasicPagingLoader<DeliveryReceipt>(proxy, grid.getStore());
	    loader.addLoadHandler(new LoadResultListStoreBinding<PagingLoadConfig, DeliveryReceipt, PagingLoadResult<DeliveryReceipt>>(grid.getStore()) {
        	@Override
        	public void onLoad(LoadEvent<PagingLoadConfig, PagingLoadResult<DeliveryReceipt>> event) {
        		super.onLoad(event);
        		grid.getSelectionModel().deselectAll();
        		edit(null);
        	}
        });
        
        grid.setLoader(loader);
        grid.setLoadMask(true);
		
		pagingToolbar.bind(loader);
	}
	
	@Override
	protected SimpleBeanEditorDriver<DeliveryReceipt, ? extends MultiSelectListAndDetailPanel<DeliveryReceipt>> getDriver() {
		return driver;
	}

	// UiFactory Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@Ignore
	@UiFactory
	StoreFilterField<DeliveryReceipt> initializeGridFilter() {
		StoreFilterField<DeliveryReceipt> f = new StoreFilterField<DeliveryReceipt>() {
			@Override
			protected boolean doSelect(Store<DeliveryReceipt> store, DeliveryReceipt parent, DeliveryReceipt item, String filter) {
				String filterInLowerCase = filter.toLowerCase().trim();
				return item.getDrNo().toLowerCase().contains(filterInLowerCase);
			}
		};
		return f;
	}
	
	/**
	 * Used to create the 'grid' field in the XML file. 
	 * 
	 * @return
	 */
	@UiFactory
	Grid<DeliveryReceipt> initializeGrid() {
		DeliveryReceiptProperties prop = DeliveryReceiptProperties.INSTANCE;
		
		List<ColumnConfig<DeliveryReceipt, ?>> columns = new ArrayList<ColumnConfig<DeliveryReceipt, ?>>();
		
		columns.add(UI.column(prop.date(), 40, "Date", UI.dateCell()));
		columns.add(UI.column(prop.drNo(), 50, "D/R No."));
		columns.add(UI.column(prop.customerCode(), 80, "Customer"));
		columns.add(UI.column(prop.salesInvoiceNo(), 50, "Invoice No."));
		columns.add(UI.column(prop.remarks(), 140, "Remarks"));
		
		ColumnModel<DeliveryReceipt> cm = new ColumnModel<DeliveryReceipt>(columns);

		GridView<DeliveryReceipt> view = new BasicGridView<DeliveryReceipt>();
		view.setAutoExpandColumn(columns.get(columns.size() - 1));
		view.setEmptyText("There are no delivery receipts to display.");
		
		Grid<DeliveryReceipt> grid = new Grid<DeliveryReceipt>(new ListStore<DeliveryReceipt>(DeliveryReceiptProperties.INSTANCE.key()), cm, view);
		grid.setAllowTextSelection(false);
		grid.getSelectionModel().setSelectionMode(SelectionMode.MULTI);
		
		grid.getStore().getSortInfo().add(new StoreSortInfo<DeliveryReceipt>(prop.date(), SortDir.DESC));
		grid.getStore().getSortInfo().add(new StoreSortInfo<DeliveryReceipt>(prop.drNo(), SortDir.DESC));
		
		initGridHandlers(grid);
		
		return grid;
	}
	
	// Logic %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	protected void edit(final DeliveryReceipt deliveryReceipt) {
		super.edit(deliveryReceipt);
		
		saveBtn.setEnabled(deliveryReceipt != null && deliveryReceipt.getSalesInvoice() == null);
		cancelBtn.setEnabled(deliveryReceipt != null);
	}
	
	// Listeners %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@UiHandler("statusMenu")
	void onStatusSelection(SelectionEvent<Item> e) {
		loadData();
	}
	
	// Called when the 'Add' button is clicked
	@UiHandler("addBtn")
	void addClicked(SelectEvent e) {
		Window w = (Window) new CreateDeliveryReceiptWindow().asWidget();
		w.show();
	}
	
	@UiHandler("createInvoiceBtn")
	void createInvoiceClicked(SelectEvent e) {
		List<DeliveryReceipt> selected = grid.getSelectionModel().getSelectedItems();
		if (selected.isEmpty()) {
			Notifications.info("Please select at least one delivery receipt.");
			return;
		}
		
		// Check that all selected DRs have the same customer
		Customer customer = null;
		for (DeliveryReceipt dr : selected) {
			// Check that all DeliveryReceipts have the same Customer
			if (customer == null) {
				customer = dr.getCustomer();
			} else {
				if (dr.getCustomer() == null || !dr.getCustomer().equals(customer)) {
					Notifications.info("The selected delivery receipts must have a single customer.");
					return;
				}
			}
			
			// Check that all DeliveryReceipts have not yet been invoiced
			if (dr.getSalesInvoice() != null && dr.getSalesInvoice().getId() != null) {
				Notifications.info("One of the selected delivery receipts has already been invoiced.");
				return;
			}
		}
		
		if (customer == null) {
			Notifications.info("The selected delivery receipts do not have a valid customer.");
			return;
		}
		
		InvoiceReceiptsWindow irw = new InvoiceReceiptsWindow(customer, customer.getSalesman(), selected);
		Window w = (Window) irw.asWidget();
		w.show();
	}
	
	@UiHandler("moreBtnMenu")
	void onMenuSelection(SelectionEvent<Item> e) {
		Item item = e.getSelectedItem();
		if (item == refreshMenuItem) {
			refreshClicked();
		} else if (item == voidMenuItem) {
			voidClicked();
		} else if (item == deleteMenuItem) {
			deleteClicked();
		} 
	}
	
	// Called when the 'Refresh' button is clicked
	void refreshClicked() {
		loadData();
	}
	
	// Called when the 'Void' button is clicked
	void voidClicked() {
		final DeliveryReceipt selected = grid.getSelectionModel().getSelectedItem();
		if (selected == null)
			return;
		
		if (selected.getSalesInvoice() != null) {
			Notifications.info("The selected delivery receipt is already invoiced. It cannot be voided.");
			return;
		}
		
		UI.confirm("Confirm Void", "Are you sure you want to void '" + selected.getDrNo() + "'?",
			new ConfirmDialogHandler() {
				@Override
				public void onYes(HideEvent event) {
//						UI.mask(mainPanel, "Voiding " + selected.getDrNo() + "...");
//						deliveryReceiptSvc().delete(selected, new Callback<DeliveryReceipt>(mainPanel) {
//							@Override
//							protected void handleSuccess(DeliveryReceipt result) {
//								grid.getStore().remove(result);
//								
//								// Clear the form editor if necessary
//								DeliveryReceipt inEditor = driver.flush();
//								if (inEditor != null && inEditor.getDrNo().equals(result.getDrNo()))
//									edit(null);
//							}
//						});
				}
			}
		);
	}
	 
	// Called when the 'Delete' button is clicked
	void deleteClicked() {
		final DeliveryReceipt selected = grid.getSelectionModel().getSelectedItem();
		if (selected == null)
			return;
		
		if (selected.getSalesInvoice() != null) {
			Notifications.info("The selected delivery receipt is already invoiced. It cannot be deleted.");
			return;
		}
		
		UI.confirm("Confirm Delete", "Are you sure you want to delete '" + selected.getDrNo() + "'?",
			new ConfirmDialogHandler() {
				@Override
				public void onYes(HideEvent event) {
					UI.mask(mainPanel, "Deleting " + selected.getDrNo() + "...");
					deliveryReceiptSvc().delete(selected, new Callback<DeliveryReceipt>(mainPanel) {
						@Override
						protected void handleSuccess(DeliveryReceipt result) {
							grid.getStore().remove(result);
							
							// Clear the form editor if necessary
							DeliveryReceipt inEditor = driver.flush();
							if (inEditor != null && inEditor.getDrNo().equals(result.getDrNo()))
								edit(null);
						}
					});
				}
			}
		);
	}
	
	// Called when the 'Save' button is clicked
	@UiHandler("saveBtn")
	void saveClicked(SelectEvent e) {
		DeliveryReceipt deliveryReceipt = driver.flush();
		if (driver.hasErrors())
			return;
		
		UI.mask(mainPanel, "Updating delivery reciept...");
		deliveryReceiptSvc().update(deliveryReceipt, new Callback<DeliveryReceipt>(mainPanel) {
			@Override
			protected void handleSuccess(DeliveryReceipt result) {
				grid.getStore().update(result);
				edit(result);
			}
		});
	}
	
	// Called when the 'Cancel' button is clicked
	@UiHandler("cancelBtn")
	void cancelClicked(SelectEvent e) {
		edit(null);
	}
	
	@UiHandler("date")
	void onDateParseError(ParseErrorEvent event) {
		Info.display("Parse Error", event.getErrorValue() + " could not be parsed as a date");
	}
	 
	// ITabItemPanel Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@Override
	public String getPanelId() {
		return SalesTasks.DELIVERY_RECEIPTS.getId();
	}
	
	@Override
	public TabItemConfig getConfig() {
		if (config == null) {
			config = new TabItemConfig("Delivery Receipts", true);
			config.setIcon(SalesResources.INSTANCE.deliveryReceipts());
		}
		return config;
	}
	
	@Override
	public void loadData() {
		UI.mask(customer, "Loading customers...");
		customerSvc().getAll(new Callback<List<Customer>>(customer) {
			@Override
			protected void handleSuccess(List<Customer> result) {
				customer.getStore().clear();
				customer.getStore().addAll(result);
			}
		});
		
		// Load data
		Log.debug("Loading delivery reciepts...");
		loader.load();
		edit(null);
	}
	
	// Helpers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	void setFilterInfo(TransactionLoadConfig config) {
		// Status
		if (uninvoicedOnlyMenuItem.isChecked())
			config.setInvoiced(false);
		else if (invoicedOnlyMenuItem.isChecked())
			config.setInvoiced(true);
		else
			config.setInvoiced(null);
	}
	
}
